﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Workflow;
using System.Workflow.ComponentModel;
using Microsoft.Crm.Sdk.Query;
using System.Workflow.ComponentModel.Compiler;
using Microsoft.Crm.Sdk.Metadata;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy.Metadata;

namespace CrmContrib.Workflow.Activities.Utilities
{
    [CrmWorkflowActivity("Count Entities By Relationship Activity", "CrmContrib \\ Utilities")]
    public class CountEntitiesByRelationshipActivity : Activity
    {
        /// <summary>
        /// The Identifier (GUID) of the parent entity.
        /// </summary>
        /// <remarks>
        /// This is an optional field. If this field is not set then the primary entity id will be the primary entity id associated with the context of the workflow.
        /// </remarks>
        [CrmInput("PrimaryEntityId")]
        [ValidationOption(ValidationOption.Optional)]
        public String PrimaryEntityId
        {
            get { return (string)base.GetValue(PrimaryEntityIdProperty); }
            set { base.SetValue(PrimaryEntityIdProperty, value); }
        }

        [CrmInput("Relationship")]
        [ValidationOption(ValidationOption.Required)]
        public String Relationship
        {
            get { return (string)base.GetValue(RelationshipProperty); }
            set { base.SetValue(RelationshipProperty, value); }
        }

        public static readonly DependencyProperty PrimaryEntityIdProperty = DependencyProperty.Register("PrimaryEntityId", typeof(string), typeof(CountEntitiesByRelationshipActivity));
        public static readonly DependencyProperty RelationshipProperty = DependencyProperty.Register("Relationship", typeof(string), typeof(CountEntitiesByRelationshipActivity));

        private Guid GetPrimaryEntityGuid(IContextService contextService)
        {
            if (!string.IsNullOrEmpty(PrimaryEntityId))
            {
                return new Guid(PrimaryEntityId);
            }
            return contextService.Context.PrimaryEntityId;
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            var contextService = executionContext.GetService<IContextService>();
            var workflowContext = contextService.Context;
            var crmService = workflowContext.CreateCrmService();
            var metaDataService = workflowContext.CreateMetadataService();
            HasError = new CrmBoolean(false);

            RetrieveRelationshipRequest relationshipRequest = new RetrieveRelationshipRequest();
            relationshipRequest.Name = Relationship;
            relationshipRequest.RetrieveAsIfPublished = false;

            RetrieveRelationshipResponse relationshipResponse = (RetrieveRelationshipResponse)metaDataService.Execute(relationshipRequest);

            string referingEntity, referingAttribute;
            List<BusinessEntity> entities;
            RetrieveMultipleRequest request;

            var PrimaryEntityGuid = GetPrimaryEntityGuid(contextService);

            if (relationshipResponse.RelationshipMetadata.RelationshipType == EntityRelationshipType.ManyToMany)
            {
                // The code to retrieve all many to many items
                // is heavily influenced by http://crm40distributewf.codeplex.com/ 

                var relationship = relationshipResponse.RelationshipMetadata as ManyToManyMetadata;

                string primaryAttribute, secondaryEntity, secondaryAttribute;
                string intersection = relationship.IntersectEntityName;
                if (relationship.Entity1LogicalName == contextService.Context.PrimaryEntityName)
                {
                    // entity1 is primary
                    primaryAttribute = relationship.Entity1IntersectAttribute;
                    secondaryEntity = relationship.Entity2LogicalName;
                    secondaryAttribute = relationship.Entity2IntersectAttribute;
                }
                else
                {
                    // entity2 is primary
                    primaryAttribute = relationship.Entity2IntersectAttribute;
                    secondaryEntity = relationship.Entity1LogicalName;
                    secondaryAttribute = relationship.Entity1IntersectAttribute;
                }

                QueryExpression query = new QueryExpression();
                LinkEntity secondaryToIntersection = new LinkEntity();
                LinkEntity intersectionToPrimary = new LinkEntity();
                ConditionExpression primaryCondition = new ConditionExpression();

                // Chain all links
                query.EntityName = secondaryEntity;
                query.LinkEntities.Add(secondaryToIntersection);
                secondaryToIntersection.LinkEntities.Add(intersectionToPrimary);
                intersectionToPrimary.LinkCriteria.Conditions.Add(primaryCondition);

                // First link
                secondaryToIntersection.LinkToEntityName = intersection;
                secondaryToIntersection.LinkFromAttributeName =
                secondaryToIntersection.LinkToAttributeName = secondaryAttribute;

                // Second link
                intersectionToPrimary.LinkToEntityName = contextService.Context.PrimaryEntityName;
                intersectionToPrimary.LinkFromAttributeName =
                intersectionToPrimary.LinkToAttributeName = primaryAttribute;

                // Condition
                primaryCondition.AttributeName = primaryAttribute;
                primaryCondition.Operator = ConditionOperator.Equal;
                primaryCondition.Values = new object[] { PrimaryEntityGuid.ToString() };

                request = new RetrieveMultipleRequest();
                request.Query = query;
                request.ReturnDynamicEntities = true;

            }
            else
            {
                // one to many 
                var relationship = relationshipResponse.RelationshipMetadata as OneToManyMetadata;
                referingEntity = relationship.ReferencingEntity;
                referingAttribute = relationship.ReferencingAttribute;

                QueryByAttribute query = new QueryByAttribute();
                query.EntityName = referingEntity;
                query.Attributes = new string[] { referingAttribute };
                query.Values = new string[] { PrimaryEntityGuid.ToString() };

                request = new RetrieveMultipleRequest();
                request.Query = query;
                request.ReturnDynamicEntities = true;
            }


            RetrieveMultipleResponse retrieveResponse = (RetrieveMultipleResponse)crmService.Execute(request);
            entities = retrieveResponse.BusinessEntityCollection.BusinessEntities;

            if (entities == null)
            {
                HasError = new CrmBoolean(true);
            }
            else
            {
                Count = new CrmNumber(entities.Count());
            }

            return base.Execute(executionContext);
        }


        [CrmOutput("Count")]
        public CrmNumber Count
        {
            get { return (CrmNumber)base.GetValue(CountProperty); }
            set { base.SetValue(CountProperty, value); }
        }
        public static readonly DependencyProperty CountProperty = DependencyProperty.Register("Count", typeof(CrmNumber), typeof(CountEntitiesByRelationshipActivity));

        [CrmOutput("Has an Error")]
        public CrmBoolean HasError
        {
            get { return (CrmBoolean)base.GetValue(HasErrorProperty); }
            set { base.SetValue(HasErrorProperty, value); }
        }

        public static readonly DependencyProperty HasErrorProperty = DependencyProperty.Register("HasError", typeof(CrmBoolean), typeof(CountEntitiesByRelationshipActivity));


    }
}
